#include <stdio.h>
#include <stdint.h> // for uint8_t int32_t ...
#include <unistd.h> // for sleep
#include <string.h> // for memset
#include <stdlib.h> // for srand rand
#include <time.h>
#include <math.h>

#include "util_bmp.h"

//输出帧路径
#define OUTPUT_DIR "./output"
//读取bmp图片
#define INPUT_BMP "./input.bmp"

//定义一块画布
#define WIDTH 720
#define HEIGHT 480
typedef union {
    uint8_t addr[WIDTH * HEIGHT * 3];
    uint8_t rgb[HEIGHT][WIDTH][3];
} Map;

//获取三角形最长边
int32_t triangle_max_line(int32_t xy[6])
{
    int32_t L1 = (xy[0] - xy[2]) * (xy[0] - xy[2]) + (xy[1] - xy[3]) * (xy[1] - xy[3]);
    int32_t L2 = (xy[0] - xy[4]) * (xy[0] - xy[4]) + (xy[1] - xy[5]) * (xy[1] - xy[5]);
    int32_t L3 = (xy[2] - xy[4]) * (xy[2] - xy[4]) + (xy[5] - xy[3]) * (xy[5] - xy[3]);
    if (L2 > L1)
    {
        if (L2 > L3)
            return (int)sqrt((float)L2);
        else
            return (int)sqrt((float)L3);
    }
    else
    {
        if (L1 > L3)
            return (int)sqrt((float)L1);
        else
            return (int)sqrt((float)L3);
    }
}

//遍历三角形,纯色填充,按坐标顺序填充红、绿、蓝三色
void triangle_fill(int32_t xy[6], Map* map)
{
    int32_t i, j, k;
    int32_t x, y;
    int32_t maxLine = triangle_max_line(xy);

    //对于三角形ABC内一点P,有 P = i*A + j*B + k*C, 且 i + j + k = 1
    //即遍历所有的i,j,k数值即可获得所有P点
    for (i = 0; i < maxLine; i += 1)
    {
        for (j = 0; j < maxLine - i; j += 1)
        {
            //这里尽可能不引入浮点运算,以提高性能
            //本来 i + j + k = 1, 现在变为 i + j + k = maxLine
            k = maxLine - i - j;
            x = (i * xy[0] + j * xy[2] + k * xy[4]) / maxLine;
            y = (i * xy[1] + j * xy[3] + k * xy[5]) / maxLine;

            //颜色配重,越靠近过顶点,越偏向其颜色
            map->rgb[y][x][0] = 255 * i / maxLine; //颜色r位置使用i配重,即三角ABC顶点A,下同
            map->rgb[y][x][1] = 255 * j / maxLine; //颜色g位置
            map->rgb[y][x][2] = 255 * k / maxLine; //颜色b位置
        }
    }
}

//遍历三角形,图片填充
void triangle_fillWithPic(int32_t xy[6], Map* map, uint8_t* pic, int32_t picWidth, int32_t picHeight, int32_t xyOfPic[6])
{
    int32_t i, j, k;
    int32_t x, y;
    int32_t maxLine = triangle_max_line(xy);

    int32_t x2, y2;
    int32_t offset;

    //对于三角形ABC内一点P,有 P = i*A + j*B + k*C, 且 i + j + k = 1
    //即遍历所有的i,j,k数值即可获得所有P点
    for (i = 0; i < maxLine; i += 1)
    {
        for (j = 0; j < maxLine - i; j += 1)
        {
            //这里尽可能不引入浮点运算,以提高性能
            //本来 i + j + k = 1, 现在变为 i + j + k = maxLine
            k = maxLine - i - j;
            x = (i * xy[0] + j * xy[2] + k * xy[4]) / maxLine;
            y = (i * xy[1] + j * xy[3] + k * xy[5]) / maxLine;

            //把i,j,k权重用到图片对应顶点,得到三角形中的坐标(x,y)对应图片中的坐标(x2,y2)
            x2 = (i * xyOfPic[0] + j * xyOfPic[2] + k * xyOfPic[4]) / maxLine;
            y2 = (i * xyOfPic[1] + j * xyOfPic[3] + k * xyOfPic[5]) / maxLine;

            //往(x,y)填充图片中(x2,y2)坐标的颜色
            offset = (y2 * picWidth + x2) * 3;
            map->rgb[y][x][0] = pic[offset++];
            map->rgb[y][x][1] = pic[offset++];
            map->rgb[y][x][2] = pic[offset++];
        }
    }
}

//设矩形图片为ABCD,则以下拆分成三角形ABC和三角形ACD分别绘制
void pic_draw(int32_t rect[8], Map* map, uint8_t* pic, int32_t picWidth, int32_t picHeight)
{
    int32_t xy[6];
    //三角形顶点坐标对应矩形图片的坐标
    int32_t xyOfPic[6];

    //目标O点
    xy[4] = (rect[0] + rect[2] + rect[4] + rect[6]) / 4;
    xy[5] = (rect[1] + rect[3] + rect[5] + rect[7]) / 4;
    //图中的O点
    xyOfPic[4] = picWidth / 2;
    xyOfPic[5] = picHeight / 2;

    //ABO三角
    xy[0] = rect[0];
    xy[1] = rect[1];
    xy[2] = rect[2];
    xy[3] = rect[3];
    xyOfPic[0] = 0;
    xyOfPic[1] = 0;
    xyOfPic[2] = picWidth - 1;
    xyOfPic[3] = 0;
    triangle_fillWithPic(xy, map, pic, picWidth, picHeight, xyOfPic);
    //BCO三角
    xy[0] = rect[2];
    xy[1] = rect[3];
    xy[2] = rect[4];
    xy[3] = rect[5];
    xyOfPic[0] = picWidth - 1;
    xyOfPic[1] = 0;
    xyOfPic[2] = picWidth - 1;
    xyOfPic[3] = picHeight - 1;
    triangle_fillWithPic(xy, map, pic, picWidth, picHeight, xyOfPic);
    //CDO三角
    xy[0] = rect[4];
    xy[1] = rect[5];
    xy[2] = rect[6];
    xy[3] = rect[7];
    xyOfPic[0] = picWidth - 1;
    xyOfPic[1] = picHeight - 1;
    xyOfPic[2] = 0;
    xyOfPic[3] = picHeight - 1;
    triangle_fillWithPic(xy, map, pic, picWidth, picHeight, xyOfPic);
    //DAO三角
    xy[0] = rect[6];
    xy[1] = rect[7];
    xy[2] = rect[0];
    xy[3] = rect[1];
    xyOfPic[0] = 0;
    xyOfPic[1] = picHeight - 1;
    xyOfPic[2] = 0;
    xyOfPic[3] = 0;
    triangle_fillWithPic(xy, map, pic, picWidth, picHeight, xyOfPic);
}

#if 1 // 拉伸图片测试

int main(void)
{
    int32_t order = 0;
    int32_t rect[8];
    Map map;

    //加载图片
    uint8_t* pic;
    int32_t picWidth, picHeight;
    pic = UtilBmpGet(INPUT_BMP, &picWidth, &picHeight, NULL);

    //随机数生成器装填时间种子
    srand(time(0));

    while (order < 10)
    {
        //清屏
        memset(map.addr, 0, WIDTH * HEIGHT * 3);
        //随机矩形,注意限制坐标范围
        rect[0] = WIDTH / 4; //rand() % (WIDTH / 2);
        rect[1] = HEIGHT / 4; //rand() % (HEIGHT / 2);
        rect[2] = rand() % (WIDTH / 2) + WIDTH / 2;
        rect[3] = rand() % (HEIGHT / 2);
        rect[4] = WIDTH * 3 / 4; //rand() % (WIDTH / 2) + WIDTH / 2;
        rect[5] = HEIGHT * 3 / 4; //rand() % (HEIGHT / 2) + HEIGHT / 2;
        rect[6] = rand() % (WIDTH / 2);
        rect[7] = rand() % (HEIGHT / 2) + HEIGHT / 2;
        printf("rect: %03d.bmp %d,%d - %d,%d - %d,%d - %d,%d \r\n", 
            order, rect[0], rect[1], rect[2], rect[3], rect[4], rect[5], rect[6], rect[7]);
        //遍历三角形,得到平面坐标数组
        pic_draw(rect, &map, pic, picWidth, picHeight);
        //输出帧图片
        UtilBmpCreate2(order++, OUTPUT_DIR, (char*)map.addr, WIDTH, HEIGHT, 3);
        sleep(1);
    }

    free(pic);
    return 0;
}

#else // 自动三角形填充测试

int main(void)
{
    int32_t order = 0;
    int32_t xy[6];
    Map map;

    //随机数生成器装填时间种子
    srand(time(0));

    while (order < 10)
    {
        //清屏
        memset(map.addr, 0, WIDTH * HEIGHT * 3);
        //随机三角形,注意限制坐标范围
        xy[0] = rand() % WIDTH;
        xy[1] = rand() % HEIGHT;
        xy[2] = rand() % WIDTH;
        xy[3] = rand() % HEIGHT;
        xy[4] = rand() % WIDTH;
        xy[5] = rand() % HEIGHT;
        printf("triangle: %03d.bmp %d,%d - %d,%d - %d,%d \r\n", 
            order, xy[0], xy[1], xy[2], xy[3], xy[4], xy[5]);
        //遍历三角形,得到平面坐标数组
        triangle_fill(xy, &map);
        //输出帧图片
        UtilBmpCreate2(order++, OUTPUT_DIR, (char*)map.addr, WIDTH, HEIGHT, 3);
        sleep(1);
    }
    return 0;
}

#endif
